package app.server.project.main.service.service.rank;

import app.server.project.core.constants.Def;
import app.server.project.main.service.bo.event.*;
import app.server.project.main.service.constants.GlobalCfgKey;
import app.server.project.main.service.db.dao.cfg.GlobalCfgDao;
import app.server.project.main.service.db.dao.cfg.RaceCfgDao;
import app.server.project.main.service.db.dao.cfg.RankRewardCfgDao;
import app.server.project.main.service.db.dao.user.UserExtLock;
import app.server.project.main.service.db.entity.cfg.RaceCfgEntity;
import app.server.project.main.service.dto.rank.CommonRankResp;
import app.server.project.main.service.dto.rank.GetRankReq;
import app.server.project.main.service.dto.rank.RankResp;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;

@Service
@Slf4j
@RequiredArgsConstructor
public class RankService {


    private final RaceCfgDao raceCfgDao;
    private final ApplicationContext applicationContext;
    private final RankRewardCfgDao rankRewardCfgDao;
    private final UserExtLock userExtLock;
    private final GlobalCfgDao globalCfgDao;
    private final NeverExpireInviteRank neverExpireInviteRank;
    private final InviteDayRank inviteDayRank;
    private final InviteWeekRank inviteWeekRank;


    private HashMap<Integer, RankBase> rankBaseHashMap = new HashMap<>();


    /**
     * 初始化日榜
     *
     * @param raceCfgEntity
     */
    private void initRank(RaceCfgEntity raceCfgEntity, int type, Class requiredType, int raceId) {
        var dayRank = (RankBase) this.applicationContext.getBean(requiredType);
        var dayRankCfg = rankRewardCfgDao.getByRaceId(type);
        dayRank.init(this.getRankId(raceCfgEntity.getId(), type), dayRankCfg.getRewardNum(), dayRankCfg.getRankNum(), raceId);
        this.rankBaseHashMap.put(dayRank.getRankId(), dayRank);
    }

    public int getRankId(int raceId, int type) {
        return Integer.parseInt(String.format("%s00%s", raceId, type));
    }

    @EventListener(value = {ApplicationReadyEvent.class, RaceCfgEvent.class})
    public void init() {
        this.raceCfgDao.getAll().forEach(v -> {
            if (this.rankBaseHashMap.containsKey(this.getRankId(v.getId(), RankType.RANK_TYPE_DAY))) {
                return;
            }
            this.initRank(v, RankType.RANK_TYPE_DAY, DayRank.class, v.getId());
            this.initRank(v, RankType.RANK_TYPE_WEEK, WeekRank.class, v.getId());
        });
    }

    @EventListener(DayRankEvent.class)
    public void onDayRankEvent(DayRankEvent event) {
        var userExtDao = this.userExtLock.getUserExtByUserExtId((event.getUserId()));
        if (userExtDao.getInnerType() == Def.True) return;
        var rankId = this.getRankId(event.getRaceId(), RankType.RANK_TYPE_DAY);
        var rank = this.rankBaseHashMap.get(rankId);
        rank.addRankVal(RankEvent.of(event, rankId));
    }

    @EventListener(DayRankInnerEvent.class)
    public void onDayRankInnerEvent(DayRankInnerEvent event) {
        var rankId = this.getRankId(event.getRaceId(), RankType.RANK_TYPE_DAY);
        var rank = this.rankBaseHashMap.get(rankId);
        rank.addRankVal(RankEvent.ofInnerDay(event, rankId));
    }

    @EventListener(WeekRankEvent.class)
    public void onWeekRankEvent(WeekRankEvent event) {
        var userExtDao = this.userExtLock.getUserExtByUserExtId((event.getUserId()));
        if (userExtDao.getInnerType() == Def.True) return;
        var rankId = this.getRankId(event.getRaceId(), RankType.RANK_TYPE_WEEK);
        var rank = this.rankBaseHashMap.get(rankId);
        rank.addRankVal(RankEvent.of(event, rankId));
    }

    @EventListener(WeekRankInnerEvent.class)
    public void onWeekRankInnerEvent(WeekRankInnerEvent event) {

        var rankId = this.getRankId(event.getRaceId(), RankType.RANK_TYPE_WEEK);
        var rank = this.rankBaseHashMap.get(rankId);
        rank.addRankVal(RankEvent.ofInnerWeek(event, rankId));
    }


    public RankResp getRank(int type, GetRankReq req, long userId) {
        var rankBase = this.rankBaseHashMap.get(this.getRankId(req.getRaceId(), type));
        var ret = rankBase.getRank(userId);
        var taskUrl = this.globalCfgDao.getStr(GlobalCfgKey.RANK_TASK_URL);
        var jump = this.globalCfgDao.getStr(GlobalCfgKey.RANK_JUMP_URL);
        var now = LocalDateTime.now();
        var tommorrow = LocalDateTimeUtil.beginOfDay(now);
        tommorrow = tommorrow.plusHours(19);
        //已经过去时间了
        if (now.isAfter(tommorrow)) {
            tommorrow = tommorrow.plusDays(1);
        }
        var day = Duration.between(now, tommorrow);

        var time = DateUtil.endOfWeek(new Date());
        time = DateUtil.beginOfDay(time);
        time = DateUtil.offsetHour(time, 19);
        if (time.isBefore(new Date())) {
            time = DateUtil.offsetDay(time, 7);
        }
        var week = Duration.between(now, DateUtil.toLocalDateTime(time));

        ret.setExtInfo(taskUrl, jump, String.valueOf(day.toSeconds()), String.valueOf(week.toSeconds()));
        return ret;
    }

    private void doUrlAndTime(CommonRankResp resp) {

        var taskUrl = this.globalCfgDao.getStr(GlobalCfgKey.RANK_Invite_URL);
        var jump = this.globalCfgDao.getStr(GlobalCfgKey.RANK_JUMP_URL);

        resp.setTaskUrl(taskUrl);
        resp.setJumpUrl(jump);


        resp.setCountdownDay(this.inviteDayRank.getNextSettlementTime());
        resp.setCountdownWeek(this.inviteWeekRank.getNextSettlementTime());
    }


    public CommonRankResp getNeverExpireInviteRank(long userId) {
        var resp = this.neverExpireInviteRank.getRank(userId);
        this.doUrlAndTime(resp);
        return resp;
    }

    public CommonRankResp getInviteDayRank(long userId) {
        var resp = this.inviteDayRank.getRank(userId);
        this.doUrlAndTime(resp);
        return resp;
    }

    public CommonRankResp getInviteWeekRank(long userId) {
        var resp = this.inviteWeekRank.getRank(userId);
        this.doUrlAndTime(resp);
        return resp;
    }
}
